/*
 * Copyright 2013-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.gateway.filter;

import java.net.URI;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;

import org.springframework.cloud.gateway.route.Route;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts;

/**
 * @author Spencer Gibb
 * RouteToRequestUrlFilter 是一个实现了 GlobalFilter 和 Ordered 接口的类。
 * 它的主要功能是根据路由信息（Route）将原始请求的 URI 转换为目标服务的完整 URL，并将结果存储在 exchange 的属性中
 * ，供后续过滤器或处理链使用。
 *
 */
public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {

	/**
	 * Order of Route to URL.
	 * 功能: 定义过滤器的执行顺序。
	 * 值: 设置为 10000，表示该过滤器的优先级较低，通常在其他过滤器之后执行。
	 * 作用: 确保在目标服务地址生成之前，已完成必要的前置过滤逻辑。
	 */
	public static final int ROUTE_TO_URL_FILTER_ORDER = 10000;

	private static final Log log = LogFactory.getLog(RouteToRequestUrlFilter.class);

	/**
	 * designed to match URI schemes (like http:, https:, ftp:, mailto:, etc.). Let's break it down:
	 * 	1）[a-zA-Z]
	 * 		Matches the first character, which must be a letter (either lowercase or uppercase).
	 * 		Example: h in http:, f in ftp:.
	 * 	2）([a-zA-Z]|\\d|\\+|\\.|-)*
	 * 		Matches zero or more of the following characters:
	 * 		Letters ([a-zA-Z])
	 * 		Digits (\\d, equivalent to [0-9])
	 * 		Plus sign (\\+)
	 * 		Period (\\.)
	 * 		Hyphen (-)
	 * 	Example: ttps in https:, tp in ftp:.
	 *	3）:
	 *		Matches a colon, which must appear at the end of the scheme.
	 * 		Example: : in http:.
	 *
	 *  4）.*
	 *  	Matches any remaining characters after the colon (the rest of the URI).
	 * 		Example: //example.com in http://example.com.
	 *
	 * 	功能: 定义一个正则表达式和对应的模式，用于匹配 URI 的方案部分（如 http:, https:, ftp: 等）。
	 * 	正则表达式解析:
	 * 		[a-zA-Z]: 匹配第一个字符，必须是字母（大小写均可）。
	 *		 ([a-zA-Z]|\\d|\\+|\\.|-)*: 匹配零个或多个允许的字符（字母、数字、加号、点号、连字符）。
	 *		 :.*: 匹配冒号后的内容（URI 的其余部分）。
	 * 作用: 检查 URI 是否包含特殊的方案（如自定义协议），并决定是否需要特殊处理。
	 */
	private static final String SCHEME_REGEX = "[a-zA-Z]([a-zA-Z]|\\d|\\+|\\.|-)*:.*";
	static final Pattern schemePattern = Pattern.compile(SCHEME_REGEX);

	/* for testing */

	/**
	 *    //方案特定部分（<scheme>: 后面、# 前面的部分）
	 *
	 *    功能: 判断 URI 是否包含另一个特殊的方案（如自定义协议）。
	 * 判断条件:
	 * schemePattern.matcher(uri.getSchemeSpecificPart()).matches(): 检查 URI 的方案特定部分是否符合正则表达式。
	 * uri.getHost() == null: 检查 URI 是否没有主机名。
	 * uri.getRawPath() == null: 检查 URI 是否没有路径部分。
	 * 作用: 确定是否需要对 URI 进行特殊处理（如解码方案特定部分）
	 * @param uri
	 * @return
	 */
	static boolean hasAnotherScheme(URI uri) {
		return schemePattern.matcher(uri.getSchemeSpecificPart()).matches() && uri.getHost() == null
				&& uri.getRawPath() == null;
	}

	@Override
	public int getOrder() {
		return ROUTE_TO_URL_FILTER_ORDER;
	}

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		//1、获取路由信息
		Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
		if (route == null) {
			return chain.filter(exchange);
		}

		//获取原始请求的 URI，并检查其是否包含编码部分。
		log.trace("RouteToRequestUrlFilter start");
		URI uri = exchange.getRequest().getURI();
		boolean encoded = containsEncodedParts(uri);
		URI routeUri = route.getUri();

		//如果路由 URI 包含特殊方案（如自定义协议），将其方案存储到 GATEWAY_SCHEME_PREFIX_ATTR 属性中。
		//将路由 URI 替换为其方案特定部分。
		if (hasAnotherScheme(routeUri)) {
			// this is a special url, save scheme to special attribute
			// replace routeUri with schemeSpecificPart
			exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR, routeUri.getScheme());
			//对URL进行解码， [<scheme>:]<scheme-specific-part>[#<fragment>]
			//比如: myprotocol://example.com/path/#dss 中的  example.com/path
			routeUri = URI.create(routeUri.getSchemeSpecificPart());
		}
		//如果路由 URI 使用负载均衡协议（lb），但没有主机名，则抛出异常。
		//防止因无效主机名（如包含下划线）导致的问题。
		if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) {
			// Load balanced URIs should always have a host. If the host is null it is
			// most likely because the host name was invalid (for example included an
			// underscore)
			throw new IllegalStateException("Invalid host: " + routeUri.toString());
		}

		//使用 UriComponentsBuilder 构建一个新的 URI，合并原始请求 URI 和路由目标 URI 的信息。
		//将合并后的 URI 存储到 GATEWAY_REQUEST_URL_ATTR 属性中。
		URI mergedUrl = UriComponentsBuilder.fromUri(uri)
			// .uri(routeUri)
			.scheme(routeUri.getScheme())
			.host(routeUri.getHost())
			.port(routeUri.getPort())
			.build(encoded)
			.toUri();
		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);
		//将请求传递给下一个过滤器继续处理。
		return chain.filter(exchange);
	}

}
